/* ----------------------------------------------------------------------
*
* Copyright (C) 2025 XiaoFei. All rights reserved.
*
* @file:        XiaoZhi_ESP8266.c
* @author:      XiaoFei
* @Date:     	18-August-2025
* @Revision: 	V1
* @Project: 	XiaoFei BSP Library
*
* -------------------------------------------------------------------- */

#include "stm32f10x.h"
#include "XiaoZhi_ESP8266.h"
#include "XiaoFei_Delay.h"
#include "stdio.h"
#include "stdarg.h"
#include "stdlib.h"
#include "string.h"

ESP8266_BufDef ESP8266_BufThis;

/* 
* @function: 	ESP8266_Init
*/
void ESP8266_Init(void)
{
    ESP8266_RCC_Usart(1);
    ESP8266_RCC_TX(1);
    ESP8266_RCC_RX(1);
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Pin = ESP8266_TX_Pin;
    GPIO_Init(ESP8266_TX_GPIO, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin = ESP8266_RX_Pin;
    GPIO_Init(ESP8266_RX_GPIO, &GPIO_InitStructure);
    USART_InitTypeDef USART_InitStructure;
    USART_InitStructure.USART_BaudRate = ESP8266_BandRate;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(ESP8266_USART, &USART_InitStructure);
    USART_ITConfig(ESP8266_USART, USART_IT_RXNE, ENABLE);
    USART_ITConfig(ESP8266_USART, USART_IT_IDLE, ENABLE);
    NVIC_PriorityGroupConfig(ESP8266_Group);
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = ESP8266_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = ESP8266_ITPre;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = ESP8266_ITSub;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    USART_Cmd(ESP8266_USART, ENABLE);
    for(unsigned int i=0;i<ESP8266_RXMax ; i++)
        ESP8266_BufThis.RXData[i] = 0x00;
    ESP8266_BufThis.flag = 0x00;
    ESP8266_BufThis.Len = 0;
}

/* 
* @function: 	ESP8266_Printf
* @parameter:	[in] format					 The parameter will be send in ESP8266_Printf.
*/
void ESP8266_Printf(char *format, ...)
{
    va_list arg;
    va_start(arg, format);
    vsprintf(ESP8266_BufThis.TXData0, format, arg);
    va_end(arg);
    for (int i = 0; ESP8266_BufThis.TXData0[i] != '\0'; i++)
    {
        USART_SendData(ESP8266_USART, ESP8266_BufThis.TXData0[i]);
        while (USART_GetFlagStatus(ESP8266_USART, USART_FLAG_TXE) == RESET);
    }
}

/* 
* @function: 	ESP8266_Scanf
* @parameter:	[in] ms					     The timeout for data retrieval.
* @parameter:	[in] hand					 Header of the frame from which data needs to be retrieved.
* @parameter:	[in] format					 The parameter will be get from ESP8266_Scanf.
* @return:		Return 0x00 on error, otherwise return 0x01 plus the number of correctly retrieved data items.
*/
unsigned char ESP8266_Scanf(unsigned int ms, char* hand, char* format, ...)
{
    char* pos;
    XiaoFei_Delay_ms(1);
    va_list arg;
    va_start(arg, format);
    unsigned char parsed = 0x3F;
    if(strstr(format, hand) == NULL)
        return 0x00;
    while(ms)
    {
        if(ESP8266_BufThis.flag == 0x00)
        {
            XiaoFei_Delay_ms(1);
            ms--;
            continue;
        }
        pos = strstr(ESP8266_BufThis.RXData, hand);
        if(pos == NULL)
        {
            XiaoFei_Delay_ms(1);
            ms--;
            continue;
        }
        parsed = vsscanf(pos, format, arg);
        break;
    }
    va_end(arg);
    if(ms == 0)
        return 0x00;
    for(unsigned int i=0;i<ESP8266_RXMax ; i++)
        ESP8266_BufThis.RXData[i] = 0x00;
    return (parsed+1);
}

/* 
* @function: 	ESP8266_ATTest
* @parameter:	[in] ms					     The timeout for data retrieval.
* @return:		Return 0x00 on error, otherwise return 0x01.
*/
unsigned char ESP8266_ATTest(unsigned int ms)
{
    unsigned char ret;
    ESP8266_Printf("AT\r\n");
    ret = ESP8266_Scanf(ms,"OK","OK");
    if(ret == 0x01)
        return 0x01;
    return 0x00;
}

/* 
* @function: 	ESP8266_TCPClient
* @parameter:	[in] WIFI					 The information of the WIFI you are going to connect to.
* @parameter:	[in] IP					     The information of the IP you are going to connect to.
*/
void ESP8266_TCPClient(ESP8266_WifiDef WIFI, ESP8266_IPDef IP)
{
    ESP8266_Printf("AT+CWMODE=3\r\n");
    while(ESP8266_Scanf(10,"OK","OK") != 0x01);
    ESP8266_Printf("AT+CWJAP=\"%s\",\"%s\"\r\n", WIFI.Name, WIFI.Password);
    while(ESP8266_Scanf(10,"OK","OK") != 0x01);
    ESP8266_Printf("AT+CIPSTART=\"TCP\",\"%s\",%s\r\n", IP.IP, IP.Port);
    while(ESP8266_Scanf(10,"OK","OK") != 0x01);
}

/* 
* @function: 	ESP8266_TCPClient_Printf
* @parameter:	[in] format					 The parameter will be send in ESP8266_TCPClient_Printf.
*/
void ESP8266_TCPClient_Printf(char *format, ...)
{
    unsigned int size = 0;
    va_list arg;
    va_start(arg, format);
    vsprintf(ESP8266_BufThis.TXData1, format, arg);
    va_end(arg);
    while(ESP8266_BufThis.TXData1[size] != '\0')
        size++;
    ESP8266_Printf("AT+CIPSEND=%d\r\n",size);
    while(ESP8266_Scanf(10,">",">") != 0x01);
    XiaoFei_Delay_us(1);
    for (int i = 0; ESP8266_BufThis.TXData1[i] != '\0'; i++)
    {
        USART_SendData(ESP8266_USART, ESP8266_BufThis.TXData1[i]);
        while (USART_GetFlagStatus(ESP8266_USART, USART_FLAG_TXE) == RESET);
    }
}

/* 
* @function: 	ESP8266_TCPClient_Scanf
* @parameter:	[in] ms					     The timeout for data retrieval.
* @parameter:	[in] format					 The parameter will be get from ESP8266_TCPClient_Scanf.
* @return:		Return 0x3F for incorrect statements, otherwise return the correct number of obtained data.
*/
unsigned char ESP8266_TCPClient_Scanf(unsigned int ms, char* format, ...)
{
    char* pos;
    va_list arg;
    va_start(arg, format);
    unsigned char parsed = 0x3F;
    while(ms)
    {
        if(ESP8266_BufThis.flag == 0x00)
        {
            XiaoFei_Delay_ms(1);
            ms--;
            continue;
        }
        pos = strstr(ESP8266_BufThis.RXData, "+IPD");
        if(pos == NULL)
        {
            XiaoFei_Delay_ms(1);
            ms--;
            continue;
        }
        while((*pos) != ':')
            pos++;
        pos++;
        parsed = vsscanf(pos, format, arg);
        break;
    }
    va_end(arg);
    if(ms == 0)
        return 0x00;
        for(unsigned int i=0;i<ESP8266_RXMax ; i++)
        ESP8266_BufThis.RXData[i] = 0x00;
    return parsed;
}

/* 
* @function: 	ESP8266_TCPServer
* @parameter:	[in] WIFI					 The WIFI information to be created by the ESP8266.
* @parameter:	[in] IP					     The IP information to be created by the ESP8266.
* @parameter:	[in] APMode					 The encryption method of the WIFI to ESP8266.
* @parameter:	[in] channel				 The channel num of the WIFI to ESP8266.
* @parameter:	[in] overtime				 The server WIFI timeout for ESP8266.
*/
void ESP8266_TCPServer(ESP8266_WifiDef WIFI, ESP8266_IPDef IP, ESP8266_APModeDef APMode, int channel, int overtime)
{
    ESP8266_Printf("AT+CWMODE=3\r\n");
    while(ESP8266_Scanf(10,"OK","OK") != 0x01);
    ESP8266_Printf("AT+CIPAP=\"%s\"\r\n",IP.IP);
    while(ESP8266_Scanf(10,"OK","OK") != 0x01);
    ESP8266_Printf("AT+CWSAP=\"%s\",\"%s\",%d,%d\r\n",WIFI.Name,WIFI.Password,APMode,channel);
    while(ESP8266_Scanf(10,"OK","OK") != 0x01);
    ESP8266_Printf("AT+CIPMUX=1\r\n");
    while(ESP8266_Scanf(10,"OK","OK") != 0x01);
    ESP8266_Printf("AT+CIPSERVER=1,%s\r\n",IP.Port);
    while(ESP8266_Scanf(10,"OK","OK") != 0x01);
    ESP8266_Printf("AT+CIPSTO=%d\r\n",overtime);
    while(ESP8266_Scanf(10,"OK","OK") != 0x01);
}

/* 
* @function: 	ESP8266_TCPServer_Scanf
* @parameter:	[in] port					 The port where your data from.
* @parameter:	[in] ms					     The timeout for data retrieval.
* @parameter:	[in] format					 The parameter will be get from ESP8266_TCPServer_Scanf.
* @return:		Return 0x3F for incorrect statements, otherwise return the correct number of obtained data.
*/
unsigned char ESP8266_TCPServer_Scanf(char* port, unsigned int ms, char* format, ...)
{
    char* pos;
    va_list arg;
    va_start(arg, format);
    unsigned char parsed = 0x3F;
    while(ms)
    {
        if(ESP8266_BufThis.flag == 0x00)
        {
            XiaoFei_Delay_ms(1);
            ms--;
            continue;
        }
        pos = strstr(ESP8266_BufThis.RXData, "+IPD");
        if(pos == NULL)
        {
            XiaoFei_Delay_ms(1);
            ms--;
            continue;
        }
        *port = atoi(pos + strlen("+IPD"));
        while((*pos) != ':')
            pos++;
        pos++;
        parsed = vsscanf(pos, format, arg);
        break;
    }
    va_end(arg);
    if(ms == 0)
        return 0x00;
    for(unsigned int i=0;i<ESP8266_RXMax ; i++)
        ESP8266_BufThis.RXData[i] = 0x00;
    return parsed;
}

/* 
* @function: 	ESP8266_TCPServer_Printf
* @parameter:	[in] port    			     The port where your data will be send.
* @parameter:	[in] format					 The parameter will be send in ESP8266_TCPServer_Printf.
*/
void ESP8266_TCPServer_Printf(int port, char *format, ...)
{
    unsigned int size = 0;
    va_list arg;
    va_start(arg, format);
    vsprintf(ESP8266_BufThis.TXData1, format, arg);
    va_end(arg);
    while(ESP8266_BufThis.TXData1[size] != '\0')
        size++;
    ESP8266_Printf("AT+CIPSEND=%d,%d\r\n",port,size);
    while(ESP8266_Scanf(10,">",">") != 0x01);
    XiaoFei_Delay_us(1);
    for (int i = 0; ESP8266_BufThis.TXData1[i] != '\0'; i++)
    {
        USART_SendData(ESP8266_USART, ESP8266_BufThis.TXData1[i]);
        while (USART_GetFlagStatus(ESP8266_USART, USART_FLAG_TXE) == RESET);
    }
}

/* 
* @function: 	ESP8266_UDP
* @parameter:	[in] WIFI					 The information of the WIFI you are going to connect to.
* @parameter:	[in] IP					     The information of the IP you are going to connect to.
*/
void ESP8266_UDP(ESP8266_WifiDef WIFI, ESP8266_IPDef IP)
{
    ESP8266_Printf("AT+RESTORE\r\n");
    while(ESP8266_Scanf(1000,"ready","ready") != 0x01)
        ESP8266_Printf("AT+RESTORE\r\n");
    ESP8266_Printf("AT+CWMODE=3\r\n");
    while(ESP8266_Scanf(10,"OK","OK") != 0x01);
    ESP8266_Printf("AT+CWJAP=\"%s\",\"%s\"\r\n", WIFI.Name, WIFI.Password);
    while(ESP8266_Scanf(10,"OK","OK") != 0x01);
    ESP8266_Printf("AT+CIPMUX=1\r\n");
    while(ESP8266_Scanf(10,"OK","OK") != 0x01);
    ESP8266_Printf("AT+CIPSTART=1,\"UDP\",\"%s\",%s,1112,0\r\n", IP.IP, IP.Port);
    while(ESP8266_Scanf(10,"OK","OK") != 0x01);
}

/* 
* @function: 	ESP8266_UDP_Printf
* @parameter:	[in] format					 The parameter will be send in ESP8266_UDP_Printf.
*/
void ESP8266_UDP_Printf(char *format, ...)
{
    unsigned int size = 0;
    va_list arg;
    va_start(arg, format);
    vsprintf(ESP8266_BufThis.TXData1, format, arg);
    va_end(arg);
    while(ESP8266_BufThis.TXData1[size] != '\0')
        size++;
    ESP8266_Printf("AT+CIPSEND=1,%d\r\n",size);
    while(ESP8266_Scanf(10,">",">") != 0x01);
    XiaoFei_Delay_us(1);
    for (int i = 0; ESP8266_BufThis.TXData1[i] != '\0'; i++)
    {
        USART_SendData(ESP8266_USART, ESP8266_BufThis.TXData1[i]);
        while (USART_GetFlagStatus(ESP8266_USART, USART_FLAG_TXE) == RESET);
    }
}

/* 
* @function: 	ESP8266_UDP_Scanf
* @parameter:	[in] ms					     The timeout for data retrieval.
* @parameter:	[in] format					 The parameter will be get from ESP8266_UDP_Scanf.
* @return:		Return 0x3F for incorrect statements, otherwise return the correct number of obtained data.
*/
unsigned char ESP8266_UDP_Scanf(unsigned int ms, char* format, ...)
{
    char* pos;
    va_list arg;
    va_start(arg, format);
    unsigned char parsed = 0x3F;
    while(ms)
    {
        if(ESP8266_BufThis.flag == 0x00)
        {
            XiaoFei_Delay_ms(1);
            ms--;
            continue;
        }
        pos = strstr(ESP8266_BufThis.RXData, "+IPD");
        if(pos == NULL)
        {
            XiaoFei_Delay_ms(1);
            ms--;
            continue;
        }
        while((*pos) != ':')
            pos++;
        pos++;
        parsed = vsscanf(pos, format, arg);
        break;
    }
    va_end(arg);
    if(ms == 0)
        return 0x00;
    for(unsigned int i=0;i<ESP8266_RXMax ; i++)
        ESP8266_BufThis.RXData[i] = 0x00;
    return parsed;
}

/* 
* @function: 	ESP8266_MQTT
* @parameter:	[in] WIFI					 The information of the WIFI you are going to connect to.
* @parameter:	[in] MQTT					 The information of your MQTT.
*/
void ESP8266_MQTT(ESP8266_WifiDef WIFI, ESP8266_MQTTDef MQTT)
{
    ESP8266_Printf("AT+RESTORE\r\n");
    while(ESP8266_Scanf(80,"ready","ready") != 0x01)
        ESP8266_Printf("AT+RESTORE\r\n");
    ESP8266_Printf("AT+CIPMUX=0\r\n");
    while(ESP8266_Scanf(10,"OK","OK") != 0x01);
    ESP8266_Printf("AT+CWMODE=1\r\n");
    while(ESP8266_Scanf(10,"OK","OK") != 0x01);
    ESP8266_Printf("AT+CWDHCP=1,1\r\n");
    while(ESP8266_Scanf(100,"OK","OK") != 0x01)
        ESP8266_Printf("AT+CWDHCP=1,1\r\n");
    ESP8266_Printf("AT+CWJAP=\"%s\",\"%s\"\r\n", WIFI.Name, WIFI.Password);
    while(ESP8266_Scanf(10,"OK","OK") != 0x01);
    ESP8266_Printf("AT+MQTTUSERCFG=0,1,\"%s\",\"%s\",\"%s\",0,0,\"\"\r\n", 
        MQTT.ClientID, MQTT.UserName, MQTT.Password);
    XiaoFei_Delay_ms(1);
    while(ESP8266_Scanf(10,"OK","OK") != 0x01);
    XiaoFei_Delay_ms(1);
    ESP8266_Printf("AT+MQTTCONN=0,\"%s\",%s,0\r\n",MQTT.Address, MQTT.port);
    while(ESP8266_Scanf(10,"OK","OK") != 0x01);
    XiaoFei_Delay_ms(1);
    ESP8266_Printf("AT+MQTTSUB=0,\"%s\",0\r\n",MQTT.SubTopic);
    while(ESP8266_Scanf(10,"OK","OK") != 0x01);
}

/* 
* @function: 	ESP8266_MQTT_Printf
* @parameter:	[in] MQTT					 The handle of the MQTT message.
* @parameter:	[in] json					 The message will be send from ESP8266 by MQTT.
*/
void ESP8266_MQTT_Printf(ESP8266_MQTTDef MQTT, char* json)
{
    ESP8266_Printf("AT+MQTTPUBRAW=0,\"%s\",%d,0,0\r\n", MQTT.PubTopic, strlen(json));
    while(ESP8266_Scanf(10,">",">") != 0x01);
    ESP8266_Printf("%s",json);
    while(ESP8266_Scanf(10,"OK","OK") != 0x01);
}

/* 
* @function: 	ESP8266_MQTT_Scanf
* @parameter:	[in] MQTT					 The handle of the MQTT message.
* @parameter:	[in] json					 The message will be recrive from ESP8266 by MQTT.
* @return:		Return 0x00 if have no data is receive, otherwise return the length of json.
*/
int ESP8266_MQTT_Scanf(ESP8266_MQTTDef MQTT, char* json)
{
    char* pos;
    int len;
    pos = strstr(ESP8266_BufThis.RXData, "+MQTTSUBRECV:0,");
    if(pos == NULL)
    	return 0x00;
    pos = pos + strlen("+MQTTSUBRECV:0,");
    if(strstr(pos, MQTT.SubTopic) == NULL)
    	return 0x00;
    pos = pos+strlen(MQTT.SubTopic)+3;
    len = atoi(pos);
    while(*pos != ',')
    	pos++;
    json = pos;
    json++;
    return len;
}

/* 
* @function: 	ESP8266_IRQHandler
* @callback:    The function will be callback when the ESP8266 receive the data.
*/
void ESP8266_IRQHandler(void)
{
    if(USART_GetITStatus(ESP8266_USART, USART_IT_RXNE) == SET)
    {
        USART_ClearITPendingBit(ESP8266_USART, USART_IT_RXNE);
        ESP8266_BufThis.flag = 0x00;
        ESP8266_BufThis.RXData[ESP8266_BufThis.Len] = USART_ReceiveData(ESP8266_USART);
        if(ESP8266_BufThis.Len <= (ESP8266_RXMax - 2))
            ESP8266_BufThis.Len++;
    }
    if(USART_GetITStatus(ESP8266_USART, USART_IT_IDLE) == SET)
    {
        USART_ClearITPendingBit(ESP8266_USART, USART_IT_IDLE);
        ESP8266_BufThis.RXData[ESP8266_BufThis.Len] = '\0';
        ESP8266_BufThis.Len = 0;
        ESP8266_BufThis.flag = 0x01;
        USART_ReceiveData(ESP8266_USART);
    }
}


